import os
from typing import Callable, Iterable, Optional

from src.db import PdfIdentity, add_or_update_path, add_tags_for_pdf, get_conn, init_db, upsert_pdf, update_pdf_description
from src.pdf_id import get_pdf_identity
from src.manifest import load_manifest_if_exists


def is_pdf(path: str) -> bool:
    name = os.path.basename(path).lower()
    return name.endswith(".pdf")


def scan_directory(db_path: str, root: str, on_progress: Optional[Callable[[str], None]] = None) -> int:
    """
    扫描目录下所有 PDF 并入库，返回处理的文件数量。
    """
    init_db(db_path)
    count = 0
    conn = get_conn(db_path)
    try:
        manifest = load_manifest_if_exists(root)
        for dirpath, _, filenames in os.walk(root):
            for fn in filenames:
                full = os.path.join(dirpath, fn)
                if not is_pdf(full):
                    continue
                try:
                    ident, _ = get_pdf_identity(full)
                    with conn:
                        pdf_id = upsert_pdf(conn, ident)
                        add_or_update_path(conn, pdf_id, full)
                        # 如果存在目录级清单，则根据最匹配项赋予标签和描述
                        if manifest is not None:
                            rel = os.path.relpath(full, root)
                            tags, description = manifest.match_tags(
                                doc_id=ident.doc_id,
                                page_count=ident.page_count,
                                first_sha1=ident.first_page_text_sha1,
                                filename=rel,
                            )
                            if tags:
                                add_tags_for_pdf(conn, pdf_id, tags)
                            if description:
                                update_pdf_description(conn, pdf_id, description)
                    count += 1
                    if on_progress:
                        on_progress(full)
                except Exception as e:
                    if on_progress:
                        on_progress(f"跳过（错误）：{full} -> {e}")
    finally:
        conn.close()
    return count


